/*****************************************************************************
 * asa: portable digital subtitle renderer
 *****************************************************************************
 * Copyright (C) 2006  David Lamparter
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 ****************************************************************************/

#include <windows.h>
#include "avisynth.h"
#include <stdio.h>

#include <csri/csri.h>

class CSRIAviSynth : public GenericVideoFilter
{
    csri_inst *inst;
    double spf;

public:
    CSRIAviSynth(PClip _child, IScriptEnvironment *env, const char *file,
                 const char *rendname, const char *rendver);
    ~CSRIAviSynth();

    enum csri_pixfmt GetPixfmt();

    PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment *env);

    static AVSValue __cdecl Create(AVSValue args, void* user_data,
                                   IScriptEnvironment* env);
};

CSRIAviSynth::CSRIAviSynth(PClip _child, IScriptEnvironment *env,
                           const char *file, const char *rendname, const char *rendver)
    : GenericVideoFilter(_child)
{
    csri_rend *r = csri_renderer_byname(rendname, rendver);
    if(!r)
    {
        if(rendver)
            env->ThrowError("Failed to load renderer \"%s\""
                            " version \"%s\"", rendname, rendver);
        else if(rendname)
            env->ThrowError("Failed to load renderer \"%s\"",
                            rendname);
        else
            env->ThrowError("Failed to load default renderer");
    }

    struct csri_fmt fmt;
    fmt.pixfmt = GetPixfmt();
    if(fmt.pixfmt == -1)
        env->ThrowError("Pixel format not supported by "
                        "AviSynth interface");

    inst = csri_open_file(r, file, NULL);
    if(!inst)
        env->ThrowError("Failed to load \"%s\"", file);

    fmt.width = vi.width;
    fmt.height = vi.height;
    if(csri_request_fmt(inst, &fmt))
    {
        csri_close(inst);
        env->ThrowError("Selected pixel format or size not supported "
                        "by selected subtitle renderer", file);
    }
    spf = (double)vi.fps_denominator / (double)vi.fps_numerator;
}

CSRIAviSynth::~CSRIAviSynth()
{
    csri_close(inst);
}

enum csri_pixfmt CSRIAviSynth::GetPixfmt()
{
    switch(vi.pixel_type)
    {
    case VideoInfo::CS_BGR24:
        return CSRI_F_BGR;
    case VideoInfo::CS_BGR32:
        return CSRI_F_BGR_;
    case VideoInfo::CS_YUY2:
        return CSRI_F_YUY2;
    case VideoInfo::CS_YV12:
        return CSRI_F_YV12;
    }
    return (enum csri_pixfmt) - 1;
}

PVideoFrame __stdcall CSRIAviSynth::GetFrame(int n, IScriptEnvironment *env)
{
    PVideoFrame avsframe = child->GetFrame(n, env);
    struct csri_frame frame;

    env->MakeWritable(&avsframe);

    frame.pixfmt = GetPixfmt();
    frame.planes[0] = avsframe->GetWritePtr();
    frame.strides[0] = avsframe->GetPitch();
    if(csri_is_yuv_planar(frame.pixfmt))
    {
        frame.planes[1] = avsframe->GetWritePtr(PLANAR_U);
        frame.strides[1] = avsframe->GetPitch(PLANAR_U);
        frame.planes[2] = avsframe->GetWritePtr(PLANAR_V);
        frame.strides[2] = avsframe->GetPitch(PLANAR_V);
    }
    if(csri_is_rgb(frame.pixfmt))
    {
        frame.planes[0] += (vi.height - 1) * frame.strides[0];
        frame.strides[0] = -frame.strides[0];
    }

    csri_render(inst, &frame, n * spf);
    return avsframe;
}

AVSValue __cdecl CSRIAviSynth::Create(AVSValue args, void* user_data,
                                      IScriptEnvironment* env)
{
    const char *rname = args.ArraySize() >= 2 ? args[2].AsString() : NULL,
                *rver = args.ArraySize() >= 3 ? args[3].AsString() : NULL;
    return new CSRIAviSynth(args[0].AsClip(), env, args[1].AsString(),
                            rname, rver);
}

extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(
    IScriptEnvironment* env)
{
    static char avs_name_f[2048];
    const char *avs_cmdname = (const char *)csri_query_ext(NULL,
                              "csri.avisynth25.command_name");
    const char *avs_name = (const char *)csri_query_ext(NULL,
                           "csri.avisynth25.name");

    if(!csri_renderer_default())
        return NULL;

    if(!avs_cmdname)
        avs_cmdname = "CSRI";
    if(!avs_name)
        avs_name = "Common Subtitle Renderer Interface";

    env->AddFunction(avs_cmdname, "cs", CSRIAviSynth::Create, 0);
    env->AddFunction(avs_cmdname, "css", CSRIAviSynth::Create, 0);
    env->AddFunction(avs_cmdname, "csss", CSRIAviSynth::Create, 0);

    snprintf(avs_name_f, sizeof(avs_name_f),
             "%s [AviSynth 2.5 front-end]", avs_name);
    return avs_name_f;
}

